﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

/// <summary>
/// Erweiterung der Klasse boardType
/// </summary>
public partial class boardType
{
    /// <summary>
    /// Leere Standardkonstruktor
    /// </summary>
    public boardType()
    {
    }

    /// <summary>
    /// Erstellt eine tiefe Kopie des uebergebenen Boards
    /// </summary>
    /// <param name="board"></param>
    public boardType(boardType board)
    {
        //this.forbidden
        if (board.forbidden != null)
        {
            this.forbidden = new positionType(board.forbidden);
        }
        //this.row
        this.row = new List<cardType>();
        foreach (cardType card in board.row)
        {
            this.row.Add(new cardType(card));
        }
        //this.shiftCard
        this.shiftCard = new cardType(board.shiftCard);
    }

    #region manipulierende Methoden

    /// <summary>
    /// Schiebt eine Karte ins Spielfeld
    /// 
    /// Anmerkung:
    /// Pins werden nicht korrekt umgesetzt, da server dann rumspackt.
    /// </summary>
    /// <param name="card">Einzuschiebende Karte</param>
    /// <param name="position">Position der einzuschiebenden Karte</param>
    /// <param name="movePins">Sollen die pins ans andere Ende des Feldes gesetzt</param>
    /// <returns>Karte, die rauskommt</returns>
    public void Push(Point position, bool movePins)
    {
        cardType _lOut = null;
        cardType[,] _lArr = this.Arr;

        //Pruefen, ob Position erlaubt und Werte setzen
        if ((position.Y == 1) || (position.Y == 3) || (position.Y == 5))
        {
            switch (position.X)
            {
                case 0:
                    _lOut = _lArr[position.Y, 6];
                    for (int i = 6; i > 0; --i) _lArr[position.Y, i] = _lArr[position.Y, i - 1];
                    _lArr[position.Y, 0] = shiftCard;
                    break;
                case 6:
                    _lOut = _lArr[position.Y, 0];
                    for (int i = 0; i < 6; ++i) _lArr[position.Y, i] = _lArr[position.Y, i + 1];
                    _lArr[position.Y, 6] = shiftCard;
                    break;
                default: throw new ArgumentException("Karte muss am Rand eingefuegt werden");
            }
        }
        else if ((position.X == 1) || (position.X == 3) || (position.X == 5))
        {
            switch (position.Y)
            {
                case 0:
                    _lOut = _lArr[6, position.X];
                    for (int i = 6; i > 0; --i) _lArr[i, position.X] = _lArr[i - 1, position.X];
                    _lArr[0, position.X] = shiftCard;
                    break;
                case 6:
                    _lOut = _lArr[0, position.X];
                    for (int i = 0; i < 6; ++i) _lArr[i, position.X] = _lArr[i + 1, position.X];
                    _lArr[6, position.X] = shiftCard;
                    break;
                default: throw new ArgumentException("Karte muss am Rand eingefuegt werden");
            }
        }
        else
        {
            throw new ArgumentException("Karte kann nur an Stelle 1, 3 oder 5 eingefuegt werden");
        }

        //noch das Feld aktualisieren
        row.Clear();
        for (int i = 0; i < 7; ++i)
        {
            for (int j = 0; j < 7; ++j)
            {
                row.Add(_lArr[i, j]);
            }
        }

        //ggf noch pins verschieben
        if (movePins)
        {
            shiftCard.pin = _lOut.pin;
            _lOut.pin = new int[0];
        }

        //forbidden aktualisieren
        this.forbidden = forbidden == null ? new positionType() : forbidden;

        this.forbidden.col = position.X;
        this.forbidden.row = position.Y;


        shiftCard = _lOut;
    }

    #endregion

    #region suchmethoden

    /// <summary>
    /// Liefert ein zweidimensionales array von boolean, die anzeigen,
    /// ob das Feld erreichbar ist.
    /// </summary>
    /// <param name="startPosition">Startposition, von der gesucht wird.</param>
    /// <returns></returns>
    public bool[,] ReachableFields(Point startPosition)
    {
        bool[,] _lReachedFields = new bool[7, 7];
        _lReachedFields[startPosition.Y, startPosition.X] = true;
        RecursiveReachableFields(this.Arr, startPosition.X, startPosition.Y, _lReachedFields);
        return _lReachedFields;
    }

    public List<Point> ReachableFieldList(Point _uStartPos)
    {
        List<Point> _lRet = new List<Point>(49);
        bool[,] _lReachableFields = ReachableFields(_uStartPos);
        for (int i = 0; i < 7; ++i)
        {
            for (int j = 0; j < 7; ++j)
            {
                if (_lReachableFields[i, j])
                {
                    _lRet.Add(new Point(j, i));
                }
            }
        }
        return _lRet;
    }

    /// <summary>
    /// Sucht rekusiv alle erreichbaren Felder.
    /// </summary>
    /// <param name="currentX"></param>
    /// <param name="currentY"></param>
    /// <param name="reachedFields"></param>
    private void RecursiveReachableFields(cardType[,] board, int currentX, int currentY, bool[,] reachedFields)
    {
        //oben
        if ((currentY > 0)
            && (board[currentY, currentX].openings.top)
            && (board[currentY - 1, currentX].openings.bottom)
            && (!reachedFields[currentY - 1, currentX]))
        {
            reachedFields[currentY - 1, currentX] = true;
            RecursiveReachableFields(board, currentX, currentY - 1, reachedFields);
        }
        //unten
        if ((currentY < 6)
            && (board[currentY, currentX].openings.bottom)
            && (board[currentY + 1, currentX].openings.top)
            && (!reachedFields[currentY + 1, currentX]))
        {
            reachedFields[currentY + 1, currentX] = true;
            RecursiveReachableFields(board, currentX, currentY + 1, reachedFields);
        }
        //links
        if ((currentX > 0)
            && (board[currentY, currentX].openings.left)
            && (board[currentY, currentX - 1].openings.right)
            && (!reachedFields[currentY, currentX - 1]))
        {
            reachedFields[currentY, currentX - 1] = true;
            RecursiveReachableFields(board, currentX - 1, currentY, reachedFields);
        }
        //rechts
        if ((currentX < 6)
            && (board[currentY, currentX].openings.right)
            && (board[currentY, currentX + 1].openings.left)
            && (!reachedFields[currentY, currentX + 1]))
        {
            reachedFields[currentY, currentX + 1] = true;
            RecursiveReachableFields(board, currentX + 1, currentY, reachedFields);
        }
    }

    /// <summary>
    /// Sucht Spieler auf Spielfeld.
    /// </summary>
    /// <param name="id">ID des SPielers</param>
    /// <returns>Position des SPielers,falls vorhanden. sons (-1/-1)</returns>
    public Point FindPlayer(int id)
    {
        Point _lRtrn = new Point(-1, -1);
        cardType[,] _lArr = this.Arr;

        for (int i = 0; i < 7; ++i)
        {
            for (int j = 0; j < 7; ++j)
            {
                if (_lArr[i, j].pin.Contains(id))
                    return new Point(j, i);
            }
        }

        return _lRtrn;
    }


    /// <summary>
    /// Bewegt den Player an eine Neue Position.
    /// Es wird keine Pruefen durchgefuehrt, ob Move moeglich ist.
    /// </summary>
    /// <param name="id">Id des zu bewegenden Players</param>
    /// <param name="_uNewPosition">Neue Position des zu bewegenden Players</param>
    public void MovePlayer(int _uId, Point _uNewPosition)
    {
        cardType _lCard;
        List<int> _lNewPins;
        Point _lOldPos;

        //Entfernen
        _lOldPos = FindPlayer(_uId);
        _lCard = Arr[_lOldPos.Y, _lOldPos.X];
        if (_lCard.pin.Contains(_uId))
        {
            _lNewPins = new List<int>();
            foreach (int i in _lCard.pin)
            {
                if (i != _uId)
                {
                    _lNewPins.Add(i);
                }
            }
            _lCard.pin = _lNewPins.ToArray();
        }

        //Hinzufuegen
        _lCard = Arr[_uNewPosition.Y, _uNewPosition.X];
        if (!_lCard.pin.Contains(_uId))
        {
            _lNewPins = new List<int>();
            _lNewPins.AddRange(_lCard.pin);
            _lNewPins.Add(_uId);
            _lCard.pin = _lNewPins.ToArray();
        }
    }

    /// <summary>
    /// Liefert alle Spieler und ihre Positionen zurueck
    /// </summary>
    /// <param name="board"></param>
    /// <returns></returns>
    public Dictionary<int, Point> FindAllPlayers()
    {
        Dictionary<int, Point> _lRtrn = new Dictionary<int, Point>();
        cardType[,] _lArr = this.Arr;

        for (int i = 0; i < 7; ++i)
        {
            for (int j = 0; j < 7; ++j)
            {
                foreach (int p in _lArr[i, j].pin)
                {
                    _lRtrn.Add(p, new Point(j, i));
                }
            }
        }
        return _lRtrn;
    }

    /// <summary>
    /// Gibt alle Schaetze mit Position zurueck
    /// </summary>
    /// <returns>Dictionary mit Schatz und Position</returns>
    public Dictionary<treasureType, Point> FindAllTreasures()
    {
        Dictionary<treasureType, Point> _lRtrn = new Dictionary<treasureType, Point>();
        cardType[,] _lArr = this.Arr;

        for (int i = 0; i < 7; ++i)
        {
            for (int j = 0; j < 7; ++j)
            {
                if (_lArr[i, j].treasureSpecified)
                {
                    _lRtrn.Add(_lArr[i, j].treasure, new Point(j, i));
                }
            }
        }
        return _lRtrn;
    }

    /// <summary>
    /// Gibt alle erreichbaren Schaetze und ihre Position zurueck
    /// </summary>
    /// <param name="startPosition">position, von der gelaufen wird</param>
    /// <returns>Erreichbare Schaetze mit Position</returns>
    public Dictionary<treasureType, Point> ReachableTreasures(Point startPosition)
    {
        Dictionary<treasureType, Point> _lAllTreasures;
        Dictionary<treasureType, Point> _lRtrn;
        bool[,] _lReachableFields;
        _lRtrn = new Dictionary<treasureType, Point>();
        _lReachableFields = this.ReachableFields(startPosition);
        _lAllTreasures = FindAllTreasures();

        //Abgleichen
        foreach (treasureType key in _lAllTreasures.Keys)
        {
            if (_lReachableFields[_lAllTreasures[key].Y, _lAllTreasures[key].X])
            {
                _lRtrn.Add(key, _lAllTreasures[key]);
            }
        }


        return _lRtrn;
    }
    #endregion
}

